package lex;

%%

%class Scanner
%type Token
%line
%column

%{
	StringBuffer string = new StringBuffer();
	int state_start_line;
	int state_start_column;
	
	private Token token(String tag, String value, int line, int column) {
		return new Token(tag, value, line, column);
	}
	
	private Token token(String tag, String value) {
		return token(tag, value, yyline, yycolumn);
	}
	
	private Token token(String tag) {
		return token(tag, yytext());
	}
	
	private void err(String reason, int line, int column) {
		{ throw new Error((line + 1) + ":" + (column + 1) + " : lexical error; " + reason ); }
	}
	
	private void err(String reason) {
		{ err(reason, yyline, yycolumn); }
	}
%}

LineTerminator = \r|\n|\r\n
InputCharacter = [^\r\n]
WhiteSpace     = {LineTerminator} | [ \t\f]

EndOfLineComment     = "//" {InputCharacter}* {LineTerminator}

CommentBegin = "/*"
CommentEnd = "*/"

Remainder = [A-Za-z0-9_]
ClassIdentifier = [A-Z]{Remainder}*
Identifier = [a-z]{Remainder}*
ErrIdentifier = _{Remainder}*

DecIntegerLiteral = (0|[1-9][0-9]*)
ErrInteger = 0[0-9]+

Keyword = class|extends|static|void|int|boolean|string|return|if|else|while|break|continue|this|new|length|true|false|null

Operators = \[|\]|\(|\)|\.|-|\*|\/|%|\+|<=|<|>=|>|==|\!=|\!|&&|\|\||=

Structure = \{|\}|;|, 

%state STRING

%state COMMENT

%%

<YYINITIAL> {
	{Keyword}			{ return token(yytext(), yytext()); }
	{ClassIdentifier}	{ return token("CLASS_ID"); }
	{Identifier}		{ return token("ID"); }
	{ErrIdentifier}		{ err("an identifier cannot start with '_'"); }
	{ErrInteger}		{ err("numbers should not have leading zeros"); }
	{DecIntegerLiteral} { return token("INTEGER"); }
	
	/* Strings */
	\"					{ string.setLength(0); string.append('\"'); yybegin(STRING); state_start_line = yyline; state_start_column = yycolumn;}
	
	/* comments */
	/*{Comment}			{ /* ignore */ }*/
	{EndOfLineComment}	{ /* ignore */ }
	{CommentBegin}		{ yybegin(COMMENT); state_start_line = yyline; state_start_column = yycolumn;}
	

	/* whitespace */
	{WhiteSpace}		{ /* ignore */ }

	/* Operators & Structure: */
	{Operators}|{Structure}			{ return token(yytext(), yytext()); }
}

<COMMENT> {
	{CommentEnd}	{yybegin(YYINITIAL);}
	<<EOF>>			{err("unterminated comment", state_start_line, state_start_column);}
	.|{LineTerminator}	{ /* ignore */ }
}

<STRING> {
	\"                             { string.append('\"');
									yybegin(YYINITIAL); 
                                    return token("STRING", string.toString(), state_start_line, state_start_column); }
	[\x20-\x21\x23-\x5B\x5D-\x7E]+	{ string.append( yytext() ); }
	\\t                            { string.append("\\t"); }
	\\n                            { string.append("\\n"); }

	\\r                            { string.append("\\r"); }
	\\\"                           { string.append("\\\""); }
	\\\\                           { string.append("\\\\"); }

	.|\n                             { err("malformed string literal", state_start_line, state_start_column); }
}

/* error fallback */
.|{LineTerminator}	{ err("invalid character '" + yytext() + '\'' ); }